/*
 * @(#) $Header$
 *
 * Copyright (C) 2007  Forklabs Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.media.jai.operator;

import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.renderable.RenderableImage;
import java.util.Collection;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.ParameterListDescriptor;
import javax.media.jai.RenderableOp;
import javax.media.jai.RenderedOp;
import javax.media.jai.registry.RenderableRegistryMode;
import javax.media.jai.registry.RenderedRegistryMode;
import ca.forklabs.media.jai.CollectionDescriptor;

/**
 * Class {@code MedianDescriptor} is an {@link OperationDescriptor}
 * describing the <em>mediancollection</em> operation.
 * <p>
 * The <em>mediancollection</em> operation takes a collection of rendered or a
 * collection of renderable source images and calculates the median of every
 * corresponding pixel. No additional parameters are required.
 * <p>
 * There is no restriction on the actual class type used to represent the source
 * collection, but all elements of the collection must be instances of
 * {@link RenderedImage} or {@link RenderableImage} depending on the mode. The
 * number of images in the collection may vary from 2 to n.  The source images
 * may have different numbers of bands and data types.
 * <p>
 * By default, the destination image bounds are the intersection of all of the
 * source image bounds. If any of the two sources are completely disjoint, the
 * destination will have a width and a height of 0. The number of bands of the
 * destination image is equal to the minimum number of bands of all the sources,
 * and the data type is the biggest data type of all the sources.
 * <p>
 * The destination pixel values are defined by the pseudocode:
 * <pre>
 * dst[x][y][b] = median(srcs[i0][x][y][b], srcs[i1][x][y][b], ..., srcs[in][x][y][b]);
 * </pre>
 *
 * <table border=1>
 * <caption>Resource List</caption>
 * <tr><th>Name</th>        <th>Value</th></tr>
 * <tr><td>GlobalName</td>  <td>MedianCollection</td></tr>
 * <tr><td>LocalName</td>   <td>MedianCollection</td></tr>
 * <tr><td>Vendor</td>      <td>ca.umontreal.iro.image.arcticice</td></tr>
 * <tr><td>Description</td> <td>Calculates the median of a collection of rendered images</td></tr>
 * <tr><td>DocURL</td>      <td>n/a</td></tr>
 * <tr><td>Version</td>     <td>$Version$</td></tr>
 * </table>
 *
 * <p>
 * No parameters are needed for this operation.
 *
 * @author   <a href="mailto:daniel.leonard at umontreal.ca?subject=ca.umontreal.iro.image.arcticice.media.jai.operator.MedianDescriptor">Daniel Léonard</a>
 * @version $Revision$
 */
public class MedianCollectionDescriptor extends CollectionDescriptor {

//---------------------------
// Class variables
//---------------------------

   /** <em>serialVersionUID</em>. */
   private static final long serialVersionUID = 2239679912038389467L;

   /** The name of this operator. */
   public static final String NAME = "MedianCollection"; //$NON-NLS-1$

   /**
    * The resource strings that provide the general documentation and specify
    * the parameter list for this operation.
    */
   private static final String[][] RESOURCES =
   {
      { "GlobalName",  MedianCollectionDescriptor.NAME, }, //$NON-NLS-1$
      { "LocalName",   MedianCollectionDescriptor.NAME, }, //$NON-NLS-1$
      { "Vendor",      "ca.umontreal.iro.image.arcticice", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "Description", MedianCollectionDescriptor.getMedianCollectionDescription(), }, //$NON-NLS-1$
      { "DocURL",      "n/a", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "Version",     "$Version$", }, //$NON-NLS-1$ //$NON-NLS-2$
   };

   /** The supported modes. */
   private static final String[] SUPPORTED_MODES =
   {
      RenderedRegistryMode.MODE_NAME,
      RenderableRegistryMode.MODE_NAME,
   };

   /** The name of the source, use default. */
   private static final String[] SOURCE_NAMES = null;

   /** The type of source for each mode. */
   private static final Class<?>[][] SOURCE_CLASSES = new Class<?>[][]
   {
      { Collection.class, },
      { Collection.class, },
   };

   /** The parameter list descriptor for all modes. */
   private static final ParameterListDescriptor PARAMETER_LIST_DESCRIPTOR = new CollectionDescriptor.EmptyParameterListDescriptor();

   /** Description of the parameters. */
   private static final ParameterListDescriptor[] PARAMETER_LIST_DESCRIPTORS = new ParameterListDescriptor[]
   {
      MedianCollectionDescriptor.PARAMETER_LIST_DESCRIPTOR,
      MedianCollectionDescriptor.PARAMETER_LIST_DESCRIPTOR,
   };


//---------------------------
// Constructor
//---------------------------

   /**
    * Constructor.
    */
   public MedianCollectionDescriptor() {
      super(RESOURCES, SUPPORTED_MODES, SOURCE_NAMES, SOURCE_CLASSES, PARAMETER_LIST_DESCRIPTORS);
      }


//---------------------------
// Overridden methods from javax.media.jai.OperationDescriptorImpl
//---------------------------

   /**
    * Validates the input source collection.
    * @param   mode   the rendering mode.
    * @param   pb   the parameter block.
    * @param   sb   string buffer for error messages.
    * @return   {@code true} if all is well, {@code false} otherwise.
    */
   @Override
   protected boolean validateSources(String mode, ParameterBlock pb, StringBuffer sb) {
      boolean is_ok = super.validateSources(mode, pb, sb);
      if (false == is_ok) {
         return is_ok;
         }

      Collection<?> images = (Collection<?>) pb.getSource(0);
      int size = images.size();
      is_ok = (2 <= size);
      if (false == is_ok) {
         String message = this.getBadCardinalityErrorMessage();
         sb.append(message);
         return is_ok;
         }

      return is_ok;
      }


//---------------------------
// Class methods
//---------------------------

   /**
    * Calculates the median of a {@link RenderedImage} collection of images.
    * @param   sources   the source images.
    * @param   hints    the rendering hints, may be {@code null}.
    * @return   the rendered result image.
    * @exception    IllegalArgumentException   if {@code sources} is
    *                                          {@code null}.
    */
   @SuppressWarnings("nls")
   public static RenderedOp create(Collection<RenderedImage> sources, RenderingHints hints) {
       String name = MedianCollectionDescriptor.NAME;
       String mode = RenderedRegistryMode.MODE_NAME;
       ParameterBlockJAI parameter_block = new ParameterBlockJAI(name, mode)
          .setSource("source0", sources);

       String operation = MedianCollectionDescriptor.NAME;
       RenderedOp image = JAI.create(operation, parameter_block, hints);
       return image;
       }

   /**
    * Calculates the median of a {@link RenderableImage} collection of images.
    * @param   sources   the source images.
    * @param   hints    the rendering hints, may be {@code null}.
    * @return   the renderable result image.
    * @exception    IllegalArgumentException   if {@code images} is
    *                                          {@code null}.
    */
   @SuppressWarnings("nls")
   public static RenderableOp createRenderable(Collection<RenderableImage> sources, RenderingHints hints) {
      String name = MedianCollectionDescriptor.NAME;
      String mode = RenderableRegistryMode.MODE_NAME;
      ParameterBlockJAI parameter_block = new ParameterBlockJAI(name, mode)
         .setSource("source0", sources);

      String operation = MedianCollectionDescriptor.NAME;
      RenderableOp image = JAI.createRenderable(operation, parameter_block, hints);
      return image;
      }


//---------------------------
// External resources methods
//---------------------------

   /**
    * Gets the median collection operation description.
    * @return   the description.
    */
   protected static String getMedianCollectionDescription() {
      String key = Resources.MEDIAN_COLLECTION_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   /**
    * Gets the error message telling that 2+ images are required.
    * @return   the error message.
    */
   protected String getBadCardinalityErrorMessage() {
      String key = Resources.MEDIAN_COLLECTION_CARDINALITY;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   }
